package com.yox.rpc.serializer;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yox.rpc.entity.RpcRequest;
import com.yox.rpc.enumeration.SerializerCode;
import com.yox.rpc.exception.SerializeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * JSON 序列化器
 * 把 对象 翻译成 字节数组，根据 字节数组 和 Class 反序列化成对象
 */
public class JsonSerializer implements CommonSerializer{

    private static final Logger logger = LoggerFactory.getLogger(JsonSerializer.class);

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public byte[] serialize(Object obj) {
        try {
            return objectMapper.writeValueAsBytes(obj); // 将对象序列化为字节数组
        } catch (JsonProcessingException e) {
            logger.error("序列化时有错误发生", e);
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Object deserialize(byte[] bytes, Class<?> clazz) {
        try {
            Object obj = objectMapper.readValue(bytes, clazz); // 根据字节数组和Class反序列化为对象

            if (obj instanceof RpcRequest) {
                obj = handleRequest(obj);
            }
            return obj;
        } catch (IOException e) {
            logger.error("反序列化时发生错误", e);
            throw new SerializeException("反序列化时发生错误");
        }
    }

    /**
     * 反序列化辅助类
     * 因为 JSON 序列化的时候会根据字段类型进行序列化，只是转换为 JSON 字符串，会丢失对象的类型信息
     * 而 RpcRequest 中的 parameters 字段是 Object[]，这个object[]类型很模糊，序列化的时候可能丢失类型信息，就会反序列化失败；
     * 所以需要用 RpcRequest 中的 ParamTypes 匹配 parameters Object数组中的每个实例的实际类是否一致，再进行纠正反序列化
     * @param obj
     * @return
     */
    private Object handleRequest(Object obj) throws IOException {
        RpcRequest rpcRequest = (RpcRequest) obj;

        // 对反序列化后的每个parameters Object数组 进行检查，如果与 ParamTypes 不是同一个类就纠正序列化
        for (int i = 0; i < rpcRequest.getParamTypes().length; i++) {
            Class<?> clazz = rpcRequest.getParamTypes()[i];
            // 如果这个 parameters 与 ParamTypes 不是同一个类，则重新将这个 object序列化，使用ParamTypes的类信息反序列化
            if (!clazz.isAssignableFrom(rpcRequest.getParameters()[i].getClass())) {
                byte[] bytes = objectMapper.writeValueAsBytes(rpcRequest.getParameters()[i]);
                rpcRequest.getParameters()[i] = objectMapper.readValue(bytes, clazz);
            }
        }
        return rpcRequest;
    }

    @Override
    public int getCode() {
        return SerializerCode.valueOf("JSON").getCode();
    }
}
